import random
from collections import Counter

# 车轮升级版
# 构建牌库
def build_deck():
    # 筒子，用 p 表示
    dots = [f"{i}p" for i in range(1, 10)] * 4
    # 索子，用 s 表示
    bamboo = [f"{i}s" for i in range(1, 10)] * 4
    # 万字，用 m 表示
    characters = ["1m", "9m"] * 4
    # 字牌
    honors = [f"{i}z" for i in range(1, 7)] * 4
    deck = dots + bamboo + characters + honors
    random.shuffle(deck)
    return deck


# 模拟牌局
def simulate_mahjong():
    deck = build_deck()
    # 选取 10 张宝牌指示牌
    indicators = deck[:10]
    deck = deck[10:]

    # 选取 13 张手牌
    hand_cards = deck[:13]
    deck = deck[13:]

    # 选取 36 张桌上的牌
    table_cards = deck[:36]
    deck = deck[36:]

    # 剩下的牌为牌山
    mountain_cards = deck

    return indicators, hand_cards, table_cards, mountain_cards


# 换牌方法
def exchange_cards(hand_cards, mountain_cards, consumed_cards, print_log=False):
    exchanged_cards = []
    mountain_index = 0
    mountain_cards_used = []
    if print_log:
        print("-" * 20 + "开始换牌" + "-" * 20)
    for _ in range(5):
        to_exchange = []
        card_count = Counter(hand_cards)
        for card in hand_cards:
            if not card.endswith('p'):
                to_exchange.append(card)
            elif card_count[card] == 4:
                # 只换第4张重复的牌
                to_exchange.append(card)
            elif card in ['1p', '2p', '8p', '9p'] and card_count[card] == 1:
                to_exchange.append(card)
            elif 3 <= int(card[:-1]) <= 7 and card_count[card] == 2 and consumed_cards.count(card) >= 2:
                # 换掉3 - 7p中已消耗2张的对子
                to_exchange.append(card)

        to_exchange = list(set(to_exchange))  # 去重
        if print_log:
            print(f"需要换掉的牌: {to_exchange}")
        for card in to_exchange:
            if card in hand_cards:
                hand_cards.remove(card)
                exchanged_cards.append(card)
                if mountain_index < len(mountain_cards):
                    new_card = mountain_cards[mountain_index]
                    hand_cards.append(new_card)
                    mountain_cards_used.append(new_card)
                    mountain_index += 1

        hand_cards.sort(key=lambda x: (x[-1], int(x[:-1])))
        if print_log:
            print(f"当前手牌: {hand_cards}")

    return hand_cards, exchanged_cards, mountain_cards[mountain_index:], mountain_cards_used


# 检查是否能组成顺子或刻子
def can_form_melds(count):
    tiles = sorted(count.keys())
    for tile in tiles:
        while count[tile] > 0:
            if count[tile] >= 3:
                count[tile] -= 3
                continue
            if tile + 1 in count and tile + 2 in count and count[tile + 1] > 0 and count[tile + 2] > 0:
                count[tile] -= 1
                count[tile + 1] -= 1
                count[tile + 2] -= 1
            else:
                return False
    return True


# 判断是否能和牌（4 组搭子 + 1 雀头）
def is_win(hand):
    if len(hand) != 14:
        return False
    hand_numbers = [int(card[:-1]) for card in hand if card.endswith('p')]
    count = Counter(hand_numbers)
    for eye in count:
        if count[eye] >= 2:
            temp_count = count.copy()
            temp_count[eye] -= 2
            if can_form_melds(temp_count):
                return True
    return False


# 检查手牌是否是3个刻子2个对子或者4个刻子1个对子
def is_valid_hand_pattern(hand):
    card_count = Counter(hand)
    triple_count = 0
    pair_count = 0
    for count in card_count.values():
        if count == 3:
            triple_count += 1
        elif count == 2:
            pair_count += 1
    return (triple_count == 3 and pair_count == 2) or (triple_count == 4 and pair_count == 1)


# 检查是否处于听牌状态，并返回可以胡的牌种类
def is_valid_wait(hand):
    waits = set()
    all_possible_cards = [f"{i}p" for i in range(1, 10)]
    for tile in all_possible_cards:
        new_hand = sorted(hand + [tile])
        if is_win(new_hand):
            waits.add(tile)
    return waits if len(waits) >= 1 else None


# 定义牌的优先级顺序
tile_priority = {'1p': 1, '9p': 2, '2p': 3, '8p': 4, '3p': 5, '7p': 6, '4p': 7, '6p': 8, '5p': 9}


# 打牌方法
def play_mahjong(hand_cards, table_cards, exchanged_cards, print_log=False):
    win_count = 0
    is_listening = False
    waiting_tiles = None
    consumed_cards = exchanged_cards.copy()
    if print_log:
        print("-" * 20 + "开始打牌" + "-" * 20)
    for index, card in enumerate(table_cards):
        pre_hand = hand_cards.copy()
        hand_cards.append(card)

        if not is_listening:
            if all(c.endswith('p') for c in pre_hand) and is_valid_hand_pattern(pre_hand):
                waiting_tiles = is_valid_wait(pre_hand)
                if waiting_tiles:
                    is_listening = True
                    if print_log:
                        print(f"首次听牌，听牌列表: {sorted(waiting_tiles)}")

        if is_listening:
            if card in waiting_tiles and index != len(table_cards) - 1:
                win_count += 1
                hand_cards.remove(card)
                consumed_cards.append(card)
                if print_log:
                    print(f"当前手牌：{pre_hand} 摸牌：{card} 打出：{card} 胡牌")
            else:
                card_count = Counter(hand_cards)
                four_of_a_kind = [c for c, count in card_count.items() if count == 4]
                if four_of_a_kind:
                    discarded_card = [c for c in hand_cards if c == four_of_a_kind[0]][-1]
                    hand_cards.remove(discarded_card)
                    consumed_cards.append(discarded_card)
                else:
                    discarded_card = hand_cards.pop()
                    consumed_cards.append(discarded_card)
                if print_log:
                    print(f"当前手牌：{pre_hand} 摸牌：{card} 打出：{discarded_card}")
        else:
            card_count = Counter(hand_cards)
            four_of_a_kind = [c for c, count in card_count.items() if count == 4]
            if four_of_a_kind:
                discarded_card = [c for c in hand_cards if c == four_of_a_kind[0]][-1]
                hand_cards.remove(discarded_card)
                consumed_cards.append(discarded_card)
            else:
                to_discard = []
                for c in hand_cards:
                    if not c.endswith('p'):
                        to_discard.append(c)
                    elif card_count[c] > 3:
                        to_discard.append(c)

                if to_discard:
                    discarded_card = to_discard[0]
                    hand_cards.remove(discarded_card)
                    consumed_cards.append(discarded_card)
                else:
                    # 先找出已经消耗掉2张及以上的牌
                    consumed_2_or_more = [c for c in hand_cards if consumed_cards.count(c) >= 2]
                    if consumed_2_or_more:
                        # 按优先级排序
                        consumed_2_or_more.sort(key=lambda x: tile_priority[x])
                        discarded_card = consumed_2_or_more[0]
                    else:
                        single_cards = [c for c, count in card_count.items() if count == 1]
                        pair_cards = [c for c, count in card_count.items() if count == 2]
                        if single_cards:
                            # 按消耗数量和优先级排序
                            single_cards.sort(key=lambda x: (consumed_cards.count(x), tile_priority[x]))
                            discarded_card = single_cards[0]
                        elif pair_cards:
                            # 对子按优先级排序
                            pair_cards.sort(key=lambda x: tile_priority[x])
                            discarded_card = pair_cards[0]
                        else:
                            discarded_card = hand_cards.pop()
                    hand_cards.remove(discarded_card)
                    consumed_cards.append(discarded_card)

                if print_log:
                    print(f"当前手牌：{pre_hand} 摸牌：{card} 打出：{discarded_card}")

        hand_cards.sort(key=lambda x: (x[-1], int(x[:-1])))

    return win_count


# 模拟多局游戏
def simulate_multi_games(num_games, print_log=False):
    total_win_count = 0
    zero_win_count = 0
    win_counts = []
    for game_num in range(1, num_games + 1):
        if print_log:
            print(f"\n--- 第 {game_num} 局游戏开始 ---")
        indicators, hand_cards, table_cards, mountain_cards = simulate_mahjong()
        if print_log:
            print("宝牌指示牌:", indicators)
            print("桌上的牌:", table_cards)
            print("牌山:", mountain_cards)
            hand_cards.sort(key=lambda x: (x[-1], int(x[:-1])))
            print("初始手牌:", hand_cards)

        # 初始化消耗的牌列表
        consumed_cards = []
        hand_cards, exchanged_cards, remaining_mountain, mountain_cards_used = exchange_cards(
            hand_cards, mountain_cards, consumed_cards, print_log
        )
        if print_log:
            print("换牌后最终手牌:", hand_cards)
            print("已换的牌:", exchanged_cards)
            print("换牌后剩余牌山:", remaining_mountain)
            print("牌山已经换到手牌中的牌:", mountain_cards_used)

        win_count = play_mahjong(hand_cards, table_cards, exchanged_cards, print_log)
        total_win_count += win_count
        win_counts.append(win_count)
        if win_count == 0:
            zero_win_count += 1

    total_games = num_games
    bust_rate = zero_win_count / total_games if total_games > 0 else 0
    average_win = total_win_count / (total_games - zero_win_count) if (total_games - zero_win_count) > 0 else 0

    print(f"总次数：{total_games}")
    print(f"暴毙数（胡牌次数为0的）：{zero_win_count}")
    print(f"暴毙率：{bust_rate}")
    print(f"均胡：（胡牌次数>0时的平均数）{average_win}")


# 模拟 1 局游戏，打开打印日志开关
simulate_multi_games(1000, print_log=False)
